package authcomment

import (
	"context"
	"errors"
	"fmt"
	"math"
	"strconv"
	"sync"
	"time"

	"blogcenter/internal/biz"
	"blogcenter/internal/middleware"
	"blogcenter/internal/model"
	"blogcenter/internal/svc"
	"blogcenter/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/redis"
	"gorm.io/gorm"
)

type GetCommentDetailLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
	CommentBlogLogic
}

const (
	commentLikeKeyPrefix = "comment:like:"
)

func NewGetCommentDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCommentDetailLogic {
	return &GetCommentDetailLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *GetCommentDetailLogic) GetCommentDetail(req *types.CommentDetailRequest) (*types.CommentDetailResponse, error) {
	// 1. 参数校验
	// 从上下文中获取用户ID
	userId, _ := l.ctx.Value(middleware.UserIdKey).(int)
	if err := l.validateParams(req); err != nil {
		return nil, &biz.BizError{
			Code:    400,
			Message: err.Error(),
		}
	}

	// 2. 尝试从Redis获取
	replies, total, err := l.getFromRedis(req)
	if err != nil || len(replies) == 0 {
		// 3. Redis无数据时回退到数据库
		replies, total, err = l.getFromDB(req)
		if err != nil {
			return nil, errors.New("获取评论失败" + err.Error())
		}

		// 4. 异步更新Redis缓存
		go l.updateRedisCache(req, replies)
	}
	// 获取每个子评论的点赞状态（批量处理）
	for i := range replies {
		reply := replies[i]
		// 查询是否点赞过该子评论
		isLiked, err := l.getIsLikeComment(userId, int(reply.ID))
		if err != nil {
			logx.WithContext(l.ctx).Errorf("获取子评论点赞状态失败: %v", err)
			continue
		}
		reply.IsLiked = isLiked
	}

	// 5. 计算总页数
	totalPage := int(math.Ceil(float64(total) / float64(req.PageSize)))

	// 6. 返回响应
	return &types.CommentDetailResponse{
		BaseResponse: types.BaseResponse{
			Code:    200,
			Message: "success",
		},
		Total:       int(total),
		CurrentPage: req.Page,
		TotalPage:   totalPage,
		Replies:     replies,
	}, nil
}

// 参数校验 (与一级评论保持一致)
func (l *GetCommentDetailLogic) validateParams(req *types.CommentDetailRequest) error {
	if req.Id == 0 {
		return errors.New("评论ID不能为空")
	}
	if req.ObjectID == 0 {
		return errors.New("对象ID不能为空")
	}
	if req.ObjectType < 0 || req.ObjectType > 1 {
		return errors.New("无效的对象类型")
	}
	if req.Page <= 0 {
		req.Page = 1
	}
	if req.PageSize <= 0 || req.PageSize > 100 {
		req.PageSize = 20
	}
	return nil
}

// 从Redis获取回复 (与一级评论结构一致)
func (l *GetCommentDetailLogic) getFromRedis(req *types.CommentDetailRequest) ([]*types.CommentResponse, int64, error) {
	// 使用时间排序的有序集合
	zsetKey := fmt.Sprintf("comment_index:ts:%d:%d:%d",
		req.ObjectType,
		req.ObjectID,
		req.Id)

	// 获取总数
	total, err := l.svcCtx.Redis.Zcard(zsetKey)
	if err != nil || total == 0 {
		return nil, 0, err
	}

	// 分页获取
	start := (req.Page - 1) * req.PageSize
	end := start + req.PageSize - 1
	commentIDs, err := l.svcCtx.Redis.Zrevrange(zsetKey, int64(start), int64(end))
	if err != nil {
		return nil, 0, err
	}

	// 批量构建评论响应
	replies, err := l.batchBuildComments(commentIDs, req.ObjectID)
	if err != nil {
		return nil, 0, err
	}

	// 完善用户和作者信息 (与一级评论一致)
	l.enrichCommentsInfo(replies, int(req.ObjectID))

	return replies, int64(total), nil
}

// 从数据库获取回复 (与一级评论结构一致)
func (l *GetCommentDetailLogic) getFromDB(req *types.CommentDetailRequest) ([]*types.CommentResponse, int64, error) {
	// 调用模型层方法
	indexes, total, err := l.svcCtx.CommentModel.FindReplies(
		l.ctx,
		req.Id,
		req.ObjectType,
		req.ObjectID,
		req.Page,
		req.PageSize,
	)
	if err != nil {
		return nil, 0, err
	}

	// 转换为响应格式
	replies, err := l.svcCtx.CommentModel.ConvertToResponse(l.ctx, indexes, req.ObjectID)
	if err != nil {
		return nil, 0, err
	}

	// 完善用户和作者信息 (与一级评论一致)
	l.enrichCommentsInfo(replies, int(req.ObjectID))

	return replies, total, nil
}

// 更新Redis缓存 (与一级评论结构一致)
func (l *GetCommentDetailLogic) updateRedisCache(req *types.CommentDetailRequest, replies []*types.CommentResponse) {
	// 1. 更新评论内容缓存
	for _, reply := range replies {
		contentKey := fmt.Sprintf("comment_content:%d", reply.ID)
		if err := l.svcCtx.Redis.Setex(contentKey, reply.Content, redisTimeout); err != nil {
			logx.WithContext(l.ctx).Errorf("更新评论内容缓存失败, key: %s, error: %v", contentKey, err)
		}
	}

	// 2. 更新索引有序集合
	tsKey := fmt.Sprintf("comment_index:ts:%d:%d:%d",
		req.ObjectType,
		req.ObjectID,
		req.Id)
	likeKey := fmt.Sprintf("comment_index:like:%d:%d:%d",
		req.ObjectType,
		req.ObjectID,
		req.Id)

	// 单独更新每个索引
	for _, reply := range replies {
		// 解析创建时间
		createTime, err := time.Parse(time.RFC3339, reply.CreateTime)
		if err != nil {
			logx.WithContext(l.ctx).Errorf("解析评论时间失败, commentID: %d, error: %v", reply.ID, err)
			continue
		}

		// 更新时间索引
		if _, err := l.svcCtx.Redis.Zadd(tsKey, int64(createTime.Unix()), strconv.Itoa(int(reply.ID))); err != nil {
			logx.WithContext(l.ctx).Errorf("更新时间索引失败, key: %s, error: %v", tsKey, err)
		}

		// 更新点赞索引
		if _, err := l.svcCtx.Redis.Zadd(likeKey, int64(reply.LikeCount), strconv.Itoa(int(reply.ID))); err != nil {
			logx.WithContext(l.ctx).Errorf("更新点赞索引失败, key: %s, error: %v", likeKey, err)
		}
	}

	// 设置过期时间
	if err := l.svcCtx.Redis.Expire(tsKey, redisTimeout); err != nil {
		logx.WithContext(l.ctx).Errorf("设置时间索引过期时间失败, key: %s, error: %v", tsKey, err)
	}
	if err := l.svcCtx.Redis.Expire(likeKey, redisTimeout); err != nil {
		logx.WithContext(l.ctx).Errorf("设置点赞索引过期时间失败, key: %s, error: %v", likeKey, err)
	}
}

// 批量构建评论响应 (与一级评论完全一致)
func (l *GetCommentDetailLogic) batchBuildComments(commentIDs []string, objectID uint) ([]*types.CommentResponse, error) {
	var comments []*types.CommentResponse
	var wg sync.WaitGroup
	var mu sync.Mutex
	sem := make(chan struct{}, 10) // 限制并发数

	for _, idStr := range commentIDs {
		wg.Add(1)
		go func(idStr string) {
			defer wg.Done()
			sem <- struct{}{}
			defer func() { <-sem }()

			id, err := strconv.ParseUint(idStr, 10, 64)
			if err != nil {
				logx.WithContext(l.ctx).Errorf("解析评论ID失败: %v", err)
				return
			}

			comment, err := l.buildCommentResponse(uint(id))
			if err != nil {
				logx.WithContext(l.ctx).Errorf("构建评论响应失败: %v", err)
				return
			}

			mu.Lock()
			comments = append(comments, comment)
			mu.Unlock()
		}(idStr)
	}

	wg.Wait()
	return comments, nil
}

// 构建单个评论响应 (与一级评论完全一致)
func (l *GetCommentDetailLogic) buildCommentResponse(commentID uint) (*types.CommentResponse, error) {
	// 获取评论索引
	index, err := l.svcCtx.CommentModel.FindIndex(l.ctx, commentID)
	if err != nil {
		return nil, fmt.Errorf("获取评论索引失败: %w", err)
	}

	// 获取评论内容
	content, err := l.getCommentContent(commentID)
	if err != nil {
		return nil, err
	}

	return &types.CommentResponse{
		ID:         commentID,
		Content:    content,
		UserID:     index.UserId,
		CreateTime: index.CreatedAt.Format(time.RFC3339),
		LikeCount:  index.LikeCount,
	}, nil
}

// 获取评论内容 (与一级评论完全一致)
func (l *GetCommentDetailLogic) getCommentContent(commentID uint) (string, error) {
	// 先从缓存获取
	key := fmt.Sprintf("comment_content:%d", commentID)
	content, err := l.svcCtx.Redis.Get(key)
	if err == redis.Nil {
		// 缓存未命中，从数据库获取
		contentModel, err := l.svcCtx.CommentModel.FindContent(l.ctx, commentID)
		if err != nil {
			return "", fmt.Errorf("获取评论内容失败: %w", err)
		}

		// 更新缓存
		if err := l.svcCtx.Redis.Setex(key, contentModel.Content, redisTimeout); err != nil {
			logx.WithContext(l.ctx).Errorf("更新评论内容缓存失败: %v", err)
		}

		return contentModel.Content, nil
	}
	if err != nil {
		return "", fmt.Errorf("获取评论内容缓存失败: %w", err)
	}

	return content, nil
}

// 完善评论信息 (与一级评论完全一致)
func (l *GetCommentDetailLogic) enrichCommentsInfo(comments []*types.CommentResponse, objectID int) {
	for _, comment := range comments {
		// 获取用户信息
		user, err := l.getUserInfo(comment.UserID)
		if err != nil {
			logx.WithContext(l.ctx).Errorf("获取用户信息失败: %v", err)
			continue
		}
		comment.UserName = user.Name
		comment.Avatar = user.Avatar

		// 检查是否是作者
		isAuthor, err := l.checkIsAuthor(comment.UserID, uint(objectID))
		if err != nil {
			logx.WithContext(l.ctx).Errorf("检查作者身份失败: %v", err)
			continue
		}
		comment.IsAuthor = isAuthor
	}
}

// 获取用户信息 (与一级评论完全一致)
// func (l *GetCommentsLogic) getUserInfo(userID int) (*types.UserBrief, error) {

// 检查是否是作者 (与一级评论完全一致)
func (l *GetCommentDetailLogic) checkIsAuthor(userID int, objectID uint) (bool, error) {
	// 实现根据业务逻辑检查
	// 示例：调用博客服务检查作者身份
	isAuthor, err := l.svcCtx.BlogModel.CheckAuthor(l.ctx, objectID, userID)
	if err != nil {
		return false, fmt.Errorf("检查作者身份失败: %w", err)
	}
	return isAuthor, nil
}

func (l *GetCommentDetailLogic) getIsLikeComment(userId int, commentId int) (bool, error) {
	var isLiked bool
	if userId < 0 {
		return false, errors.New("用户ID不能小于0")
	}
	// 先尝试从Redis获取点赞状态
	likeKey := fmt.Sprintf("%s%d:%d", commentLikeKeyPrefix, commentId, userId)
	if val, err := l.svcCtx.Redis.Get(likeKey); err == nil && val == "1" {
		isLiked = true
	} else if err != nil && err != redis.Nil {
		logx.Errorf("获取Redis点赞状态失败, key: %s, error: %v", likeKey, err)
	}

	// Redis未命中则查询数据库
	if !isLiked {
		var likeRecord model.CommentLike
		err := l.svcCtx.DB.Where("comment_index_id = ? AND user_id = ?", commentId, userId).
			First(&likeRecord).Error
		if err == nil && likeRecord.LikeAction == 1 {
			isLiked = true
			// 异步写入Redis缓存
			go func() {
				if err := l.svcCtx.Redis.Setex(likeKey, "1", int((24 * time.Hour).Seconds())); err != nil {
					logx.Errorf("写入点赞状态到Redis失败, key: %s, error: %v", likeKey, err)
				}
			}()
		} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			logx.Errorf("查询数据库点赞状态失败, blogId: %d, userId: %d, error: %v",
				commentId, userId, err)
			return false, err
		}
	}
	return isLiked, nil
}
